﻿using System.Threading;
using System;
using System.Collections;
using System.Collections.Generic;

public class ThreadManager : Manager
{
    int mNextThreadJobID;
    int NextThreadJobID
    {
        get
        {
            return mNextThreadJobID++;
        }
    }

    Dictionary<int, ThreadJob> mThreadJobs;

    public ThreadManager()
    {
        mNextThreadJobID = 0;
        mThreadJobs = new Dictionary<int, ThreadJob>();
    }

    public ThreadJob SetJob<T>(Func<T> job)
    {
        lock (mThreadJobs)
        {
            int jobID = NextThreadJobID;
            ThreadJob threadJob = new ThreadJob(jobID);
            mThreadJobs[jobID] = threadJob;
            ThreadPool.QueueUserWorkItem((obj) =>
            {
                try
                {
                    object result = job();
                    OnResultCallback(jobID, result);
                }
                catch(Exception e)
                {
                    UnityEngine.Debug.LogError("Job Exception: " + e.ToString());
                    OnErrorCallback(jobID, e);
                }
            });
            return threadJob;
        }
    }

    public ThreadJob SetJob(Action job)
    {
        lock (mThreadJobs)
        {
            int jobID = NextThreadJobID;
            ThreadJob threadJob = new ThreadJob(jobID);
            mThreadJobs[jobID] = threadJob;
            ThreadPool.QueueUserWorkItem((obj) =>
            {
                try
                {
                    job();
                    OnResultCallback(jobID);
                }
                catch(Exception e)
                {
                    UnityEngine.Debug.LogError("Job Exception: " + e.ToString());
                    OnErrorCallback(jobID, e);
                }
            });
            return threadJob;
        }
    }

    public void CancelJob(int jobID)
    {
        lock (mThreadJobs)
        {
            if (mThreadJobs.ContainsKey(jobID))
            {
                mThreadJobs.Remove(jobID);
            }
        }
    }
	
    public void OnResultCallback(int jobID, object result = null)
    {
        lock(mThreadJobs)
        {
            if(mThreadJobs.ContainsKey(jobID))
            {
                mThreadJobs[jobID].SetResult(result);
                mThreadJobs.Remove(jobID);
            }
        }
    }

    public void OnErrorCallback(int jobID, Exception e)
    {
        lock (mThreadJobs)
        {
            if (mThreadJobs.ContainsKey(jobID))
            {
                mThreadJobs[jobID].SetError(e);
                mThreadJobs.Remove(jobID);
            }
        }
    }

}

public class ThreadJob : IEnumerator
{
    object result;
    Mutex mLock;
    bool finished = false;
    public int JobID
    {
        private set;get;
    }

    public Exception error
    {
        private set;get;
    }

    public ThreadJob(int jobID)
    {
        mLock = new Mutex();
        JobID = jobID;
    }


    public object Current
    {
        get
        {
            mLock.WaitOne();
            object tmp = result;
            mLock.ReleaseMutex();
            return tmp;
        }
    }

    public bool MoveNext()
    {
        mLock.WaitOne();
        bool ret = result == null;
        mLock.ReleaseMutex();
        return !finished;
    }

    public void Reset()
    {
        mLock.WaitOne();
        result = null;
        finished = false;
        mLock.ReleaseMutex();
    }

    public void SetResult(object result)
    {
        mLock.WaitOne();
        this.result = result;
        finished = true;
        mLock.ReleaseMutex();
    }

    public void SetError(Exception e)
    {
        mLock.WaitOne();
        this.error = e;
        finished = true;
        mLock.ReleaseMutex();

    }
}
